
#include "../include/GPCompute.h"

#include <windows.h>
#include <libpngIO.h>

#define SCREEN_WIDTH		256
#define SCREEN_HEIGHT		256
#define TEXTURE_WIDTH		256
#define TEXTURE_HEIGHT		256

void DoCounter( HWND hWnd ) {
	static DWORD t;
	static LONG count;
	DWORD currtime = GetTickCount();
	if ( t==0 ) t = currtime;
	if ( t + 1000 < currtime ) { static int i;
		static char wndtxt[260], strfps[260];
		if (i==0) { i=1; GetWindowTextA( hWnd, wndtxt, MAX_PATH ); }
		wsprintfA( strfps, "%s - FPS:%d", wndtxt, count*1000/(currtime-t) );
		SetWindowTextA( hWnd, strfps );
		t += 1000; count = 0;
	}
	++count;
}

HRESULT LoadShader( LPDWORD* pdwShader, const char* filename )
{
	HANDLE p, f;
	p = CreateFileA( filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
	if ( INVALID_HANDLE_VALUE == p ) {
		return E_FAIL;
	}
	f = CreateFileMappingA( p, NULL, PAGE_READONLY, 0, 0, NULL );
	if ( NULL == f ) {
		return E_FAIL;
	}
	*pdwShader = (LPDWORD)MapViewOfFile( f, FILE_MAP_READ, 0, 0, 0 );
	if ( NULL == *pdwShader ) {
		return E_FAIL;
	}
	CloseHandle( f );
	CloseHandle( p );
	return S_OK;
}
void UnloadShader( DWORD* pdwShader )
{
	UnmapViewOfFile( (LPCVOID)pdwShader );
}

int value = 1;
LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
    switch( msg )
    {
		case WM_TIMER:
			value = 1 - value;
			return 0;

        case WM_DESTROY:
            PostQuitMessage( 0 );
            return 0;
    }

    return DefWindowProc( hWnd, msg, wParam, lParam );
}

INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE _1, LPSTR _2, INT cmdShow )
{
	HGPGPU hgpgpu;
	HWND hWnd; GP_CONTEXT ctx; RECT rect = { 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, };

	// Register the window class
    WNDCLASSEXA wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L, /*hInst*/NULL, NULL, NULL, NULL, NULL,
                      "GP Tutorial", NULL };
	(void*)(_1, _2);
	wc.hInstance = hInst; 
    RegisterClassExA( &wc );

    // Create the application's window
    hWnd = CreateWindowA( "GP Tutorial", "02.Combination",
                      WS_OVERLAPPEDWINDOW, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT,
                      GetDesktopWindow(), NULL, wc.hInstance, NULL );

	// adjust window size ... 
	AdjustWindowRect( &rect, WS_OVERLAPPEDWINDOW, FALSE );
	MoveWindow( hWnd, 300+rect.left, 300+rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE );

	// Prepare the context : 
	ctx.width = SCREEN_WIDTH;
	ctx.height = SCREEN_HEIGHT;
	ctx.colortype = TYPE_COLORALPHA;
	ctx.colorprecision = PREC_IN_32;
	ctx.depthtype = TYPE_DEPTH;
	ctx.depthprecision = PREC_IN_16;
	ctx.canvas = (void*)hWnd;

    // Initialize Direct3D
    if( GpComputeInit( &hgpgpu, &ctx, 0 ) >= 0 )
    {
		MSG msg = {0};

		int ret, i, j, k; HGPARRAY harray; void* data; long pitch;
		HGPVRRAY hvrray[2]; // for left and right pixel combination 
		GP_CALCSTATE state = {0};
		GP_RECT rc; HGPSHAPERECT vrc;
		DWORD vshid, pshid=0xFFFFFFFF, pshid1, pshid2; LPDWORD p;

		float control[] = { 
			1.0f, 1.0f, 0.0f, 0.0f, 
			0.0f, 1.0f, 0.0f, 0.0f, 
		};
		const char* pngfilename[] = { "2a.png", "2b.png", };

		ret = GpComputeAllocate( hgpgpu, TEXTURE_WIDTH, TEXTURE_HEIGHT, 
			TYPE_COLORALPHA, PREC_IN_32, OPMODE_READ, &harray );
		if ( ret < 0 ) OutputDebugStringA( "Failed Allocate Memory!\n" );

		ret = GpComputeGetPointer( harray, &data, &pitch );
		if ( ret < 0 ) OutputDebugStringA( "Failed GetPointer!\n" );

		for ( k = 0; k < 2; ++k ) {
			PNGStruct pnginfo; LPBYTE pData, pSrc;
			// load resource 
			ret = LoadPNGFileA( pngfilename[k], &pnginfo ); if ( ret < 0 ) OutputDebugStringA( "Failed Load PNGFile!\n" );
			// copy data to array 
			pData = (LPBYTE)data; pSrc = pnginfo.pData;
			for ( i=0; i<TEXTURE_HEIGHT; ++i ) {
				for ( j=0; j<TEXTURE_WIDTH; ++j ) {
					unsigned long n = *((DWORD*)pSrc+j);
				//	A R G B				 // A B G R
					*((DWORD*)pData+j) = ((n&0xFF000000))|((n&0x00FF0000)>>16)|((n&0x0000FF00))|((n&0x000000FF)<<16);
				}
				pSrc += pnginfo.width*sizeof(DWORD), pData += pitch;
			}
			FreePNGFile( &pnginfo );
			// texture prepare 
			ret = GpComputeAllocateVram( hgpgpu, TEXTURE_WIDTH, TEXTURE_HEIGHT, 
				TYPE_COLORALPHA, PREC_IN_32, OPMODE_READ, &hvrray[k] );
			if ( ret < 0 ) OutputDebugStringA( "Failed Allocate VideoMemory!\n" );
			// copy to device-card texture 
			ret = GpComputeSubmit( hgpgpu, harray, hvrray[k], 0, 0 );
			if ( ret < 0 ) OutputDebugStringA( "Failed Submit VideoMemory!\n" );
			// set the texture : 
			ret = GpComputeSelectSourceUnit( hgpgpu, hvrray[k], k ); 
			if ( ret < 0 ) OutputDebugStringA( "Failed SetTexture!\n" );
		}

		// vertex prepare 
		rc.pt1.x = -1.0f, rc.pt1.y =  1.0f;
		rc.pt2.x =  1.0f, rc.pt2.y = -1.0f;
		ret = GpComputeShapeRect( hgpgpu, &rc, 1, &vrc );
		if ( ret < 0 ) OutputDebugStringA( "Failed Create Vertex Rectangle!\n" );
		ret = GpComputeSelectSourceShape( hgpgpu, vrc );
		if ( ret < 0 ) OutputDebugStringA( "Failed SetStreamSource and Index!\n" );

		// shader programe 
		if (FAILED(LoadShader( &p, "GPComputeCombine.vso" ))) 
			OutputDebugStringA( "Failed Allocate Load VertexShader!\n" );
		ret = GpComputeCreateProgram( hgpgpu, PROGRAM_VERTEX, &vshid, p );
		if ( ret < 0 ) OutputDebugStringA( "Failed Create VertexShader!\n" );
		UnloadShader( p );
		ret = GpComputeSelectProgram( hgpgpu, PROGRAM_VERTEX, vshid );
		if ( ret < 0 ) OutputDebugStringA( "Failed Select VertexShader!\n" );

		if (FAILED(LoadShader( &p, "GPComputeCombine1.pso" )))
			OutputDebugStringA( "Failed Allocate Load PixelShader" );
		ret = GpComputeCreateProgram( hgpgpu, PROGRAM_PIXEL, &pshid1, p );
		if ( ret < 0 ) OutputDebugStringA( "Failed Create PixelShader!\n" );
		UnloadShader( p );
		
		if (FAILED(LoadShader( &p, "GPComputeCombine2.pso" )))
			OutputDebugStringA( "Failed Allocate Load PixelShader" );
		ret = GpComputeCreateProgram( hgpgpu, PROGRAM_PIXEL, &pshid2, p );
		if ( ret < 0 ) OutputDebugStringA( "Failed Create PixelShader!\n" );
		UnloadShader( p );

		// render state : 
		state.red_write = state.green_write = state.blue_write = 1;
		state.depth_test = 1; // only test, no update 

		ret = GpComputeSetProp( hgpgpu, &state );
		if ( ret < 0 ) OutputDebugStringA( "Failed Set RenderState!\n" );

		// Show the window 
		ShowWindow( hWnd, cmdShow );
		UpdateWindow( hWnd );

		SetTimer( hWnd, 0, 1000, NULL );
        while( msg.message!=WM_QUIT )
        {
            if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
            {
                TranslateMessage( &msg );
                DispatchMessage( &msg );
            }
            else
            {
				GP_PREPARE prepare = {0};
				DoCounter( hWnd );
				
				if ( value ) {
					if ( pshid != pshid1 ) {
						pshid = pshid1;
						GpComputeSelectProgram( hgpgpu, PROGRAM_PIXEL, pshid1 );
					}
				} else {
					if ( pshid != pshid2 ) {
						pshid = pshid2;
						GpComputeSelectProgram( hgpgpu, PROGRAM_PIXEL, pshid2 );
					}
				}

				ret = GpComputeSetConstants( hgpgpu, PROGRAM_PIXEL, 0, control, 2 );
				if ( ret < 0 ) OutputDebugStringA( "Failed SetConstants!\n" );

				prepare.flagmask.color_clear = 
				prepare.flagmask.depth_clear = 1;
				prepare.value_color = 0xFF0000FF; // blue 
				prepare.value_depth = 0.5f;
				ret = GpComputeBeginTarget( hgpgpu, NULL, &prepare );
				if ( ret < 0 ) OutputDebugStringA( "Failed BeginScene!\n" );
				ret = GpComputeCalculateRect( hgpgpu, 0, 0, NULL ); // draw all 
				if ( ret < 0 ) OutputDebugStringA( "Failed RenderToTexture!\n" );
				ret = GpComputeEndTarget( hgpgpu, 1 ); // with present
				if ( ret < 0 ) OutputDebugStringA( "Failed EndScene!\n" );
            }
        }
		KillTimer( hWnd, 0 );

		GpComputeDeleteProgram( hgpgpu, PROGRAM_PIXEL, pshid );
		GpComputeDeleteProgram( hgpgpu, PROGRAM_VERTEX, vshid );
		
		GpComputeDeallocateVram( hgpgpu, hvrray[1] );
		GpComputeDeallocateVram( hgpgpu, hvrray[0] );
		GpComputeDeallocate( hgpgpu, harray );

		GpComputeShapeFree( hgpgpu, vrc );

		GpComputeFree( hgpgpu );
    }

	DestroyWindow( hWnd );
    // Clean up everything and exit the app
    UnregisterClassA( "GP Tutorial", wc.hInstance );
    return 0;
}
